/*
 * Copyright (C) 2014 luke.
 * 
 * This file is part of Silvie.
 * 
 * Silvie is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Silvie is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Silvie.  If not, see <http://www.gnu.org/licenses/>.
 */

package silvie.resources.chrmodel.structures;

import java.awt.Point;

import silvie.resources.chrmodel.chunks.Chunk0x7f02;
import utils.DataReader;

/**
 * Represents the "polygon" structure used in the 0x7f02 shape chunks. A CHR
 * polygon contains the indices of the vertices that make up the polygon and
 * each vertex's texture coordinate information, which can be (x,y) coordinates
 * or a color index in fixed.pal.
 * 
 * @author luke
 */
public class Polygon {
	private static final int PALETTE_COLOR_U = 1;
	private static final int TEX_COORD_V_OFFSET = 14;
	private static final int TEX_COORD_U_OFFSET = 10;
	private static final int MAX_COLOR_INDEX = 255;
	private static final int PALETTE_INDEX_OFFSET = 8;
	private static final int NO_TEXTURE_OFFSET_INDEX = 255;
	private byte[] polygonData;
	private int verticeCount;
	private int textureOffsetIndex;
	private int pixelSearchStart;
	private int textureWidth;
	private short[] texturePixelIndices;
	private int[] shapeVertexIndices;
	private Point[] relativeUVs;

	/**
	 * Polygon constructor.
	 * 
	 * @param polygonData the polygon's byte array.
	 * @param verticeCount the number of vertices in this polygon.
	 * @param textureOffsetIndex the index of the "subtexture" used by this
	 *        polygon.
	 */
	public Polygon(byte[] polygonData, int verticeCount, int textureOffsetIndex) {
		this.polygonData = polygonData;
		this.verticeCount = verticeCount;
		this.shapeVertexIndices = new int[this.verticeCount];
		this.relativeUVs = new Point[this.verticeCount];
		// This fixes some models like BOOTS2.CHR
		this.textureOffsetIndex = textureOffsetIndex != NO_TEXTURE_OFFSET_INDEX ? textureOffsetIndex : 0;
		this.texturePixelIndices = null;
	}

	/**
	 * Decodes the polygon's data. {@link #setPixelSearchStart(int)},
	 * {@link #setTexturePixelIndices(short[])}, and
	 * {@link #setTextureWidth(int)} must be called first.
	 */
	public void decode() {
		for (int i = 0; i < this.verticeCount; i++) {
			this.shapeVertexIndices[i] = DataReader.readInt(this.polygonData, Chunk0x7f02.POLYGON_VERTEX_INFO_SIZE * i);
			int paletteColorIndex = DataReader.readShort(this.polygonData, Chunk0x7f02.POLYGON_VERTEX_INFO_SIZE * i + PALETTE_INDEX_OFFSET);
			int paletteColorV = -1;
			int u, v;
			// TODO Some models need more texture work
			if (paletteColorIndex > 0 && paletteColorIndex < MAX_COLOR_INDEX) {
				int j = 0;
				for (int k = this.pixelSearchStart; k < this.texturePixelIndices.length; k += this.textureWidth, j++) {
					if (paletteColorIndex == this.texturePixelIndices[k]) {
						paletteColorV = j;
						break;
					}
				}
			}
			if (paletteColorV != -1) {
				u = PALETTE_COLOR_U;
				v = paletteColorV;
			} else {
				u = DataReader.readShort(this.polygonData, TEX_COORD_U_OFFSET + Chunk0x7f02.POLYGON_VERTEX_INFO_SIZE * i);
				v = DataReader.readShort(this.polygonData, TEX_COORD_V_OFFSET + Chunk0x7f02.POLYGON_VERTEX_INFO_SIZE * i);
			}
			this.relativeUVs[i] = new Point(u, v);
		}
	}

	public int getTextureOffsetIndex() {
		return this.textureOffsetIndex;
	}

	public void setPixelSearchStart(int pixelSearchStart) {
		this.pixelSearchStart = pixelSearchStart;
	}

	public void setTexturePixelIndices(short[] texturePixelIndices) {
		this.texturePixelIndices = texturePixelIndices;
	}

	public void setTextureWidth(int textureWidth) {
		this.textureWidth = textureWidth;
	}

	public int getVerticeCount() {
		return this.verticeCount;
	}

	public int[] getShapeVertexIndices() {
		return this.shapeVertexIndices;
	}

	public Point[] getRelativeUVs() {
		return this.relativeUVs;
	}
}
